import { Box3, Line3, Material, Mesh, Object3D, Raycaster, Vector3 } from "three";
import { Actor, GuandaoActor, MachineActor } from "./SceneLoader";
import { pointOnSegment, traverseObject } from "./Utils";
import { ActorJsonList, BoxInfo, LinkInfo, LinkInfoV2, PipeDir, VirtualMachinePart, VirtualPipe, VirtualPipePoint, WorkState } from "./Types";

const _vec3_1 = new Vector3();
const _vec3_2 = new Vector3();
const _line3_target = new Vector3();
const _check_pos_1 = new Vector3();
const _path_pos_2 = new Vector3();
const _path_pos_3 = new Vector3();

const box = new Box3();

export class Simulate {
    workState: WorkState = WorkState.INIT_GRAPH;

    guandoToPoint: Map<GuandaoActor, VirtualPipePoint[]> = new Map();
    pointToGuandao: Map<VirtualPipePoint, GuandaoActor> = new Map();
    guandaoToPump: Map<GuandaoActor, Actor> = new Map();
    boxInfo: Map<MachineActor, {
        w: BoxInfo | undefined,
        b: BoxInfo | undefined
    }> = new Map();
    machineMeshs: Map<Mesh, VirtualMachinePart> = new Map();

    private __fixDirQueue = [];
    private __fixedGuanDao: Map<GuandaoActor, boolean> = new Map();
    //判断递归是否重复检测了
    private __tempFixGuandao: Map<GuandaoActor, boolean> = new Map();
    radiusMap: Map<string, GuandaoActor> = new Map<string, GuandaoActor>();

    allVirtualMachine: VirtualMachinePart[] = [];
    allPoints: VirtualPipePoint[] = [];
    pumps: Actor[] = [];
    allPipeActors: GuandaoActor[] = [];

    data: { [key: string]: object } = {}

    workIdx: number = 0;
    machines: MachineActor[] = [];

    raycast: Raycaster = new Raycaster();
    constructor(data: any) {
        this.data = data;
        this.allPipeActors = this.data['guandao'] as GuandaoActor[];
    }

    initData() {
        VirtualPipePoint.PipePointID = 0;
        this.workState = WorkState.INIT_GRAPH;
        this.guandoToPoint.clear();
        this.allPoints = [];

        this.__fixDirQueue = [];
        this.__fixedGuanDao = new Map();
        this.__tempFixGuandao = new Map();

        traverseObject(this.data.__root as any as Actor, (obj) => {
            let actor = obj as Actor;
            if (actor.isPump) {
                if (this.pumps.indexOf(actor) === -1) {
                    this.pumps.push(actor);
                }
            }

            if (actor.isMachine) {
                this.machines.push(actor);
            }
            return true;
        })


        this.__setupBox();
    }

    private __setupBox() {
        for (let i = 0; i < this.machines.length; i++) {
            const machine = this.machines[i];

            let w_part: BoxInfo | undefined;
            let b_part: BoxInfo | undefined;

            let aps: { [key: string]: Object3D } = {}
            machine.traverse((mesh) => {
                if (mesh.name.startsWith('@AP_')) {
                    aps[mesh.name] = mesh;
                }

                if ((mesh as Mesh).isMesh) {
                    let matname = ((mesh as Mesh).material as Material).name;
                    if (matname.indexOf("IRON_") >= 0) {
                        if (matname.indexOf("IRON_WHITE") >= 0) {
                            w_part = {
                                max: new Vector3(),
                                min: new Vector3(),
                                mesh: mesh as Mesh,
                                name: matname,
                            }
                        }
                        else {
                            b_part = {
                                max: new Vector3(),
                                min: new Vector3(),
                                mesh: mesh as Mesh,
                                name: matname,
                            }
                        }

                    }
                }

            })

            if (aps['@AP_A2'] && b_part) {
                _vec3_1.copy(aps['@AP_A2'].position)
                aps['@AP_A2'].parent?.localToWorld(_vec3_1)
                b_part.min.copy(_vec3_1)
            }
            if (aps['@AP_A1'] && b_part) {
                _vec3_1.copy(aps['@AP_A1'].position)
                aps['@AP_A1'].parent?.localToWorld(_vec3_1)
                b_part.max.copy(_vec3_1)
            }

            if (aps['@AP_B2'] && w_part) {
                _vec3_1.copy(aps['@AP_B2'].position)
                aps['@AP_B2'].parent?.localToWorld(_vec3_1)
                w_part.min.copy(_vec3_1)
            }
            if (aps['@AP_B1'] && w_part) {
                _vec3_1.copy(aps['@AP_B1'].position)
                aps['@AP_B1'].parent?.localToWorld(_vec3_1)
                w_part.max.copy(_vec3_1)
            }

            this.boxInfo.set(machine, {
                b: b_part,
                w: w_part
            })
        }
    }

    private __checkHasWaterInPath(posa: Vector3, posb: Vector3, radius: number, output: Vector3) {
        for (let i = 0; i < this.pumps.length; i++) {
            const pump = this.pumps[i];
            pump.getWorldPosition(_check_pos_1);
            if (pointOnSegment(_check_pos_1, posa, posb, radius, output)) {
                return pump;
            }
        }
    }

    private __isVisible(obj: Object3D): boolean {
        if (!obj.visible) {
            return false;
        }
        if (obj.parent) {
            return this.__isVisible(obj.parent);
        }
        return true;
    }

    //返回连接的节点，可能是已经存在的，可能是新创建的
    private __checkPipeV3(pipe: GuandaoActor, start: Vector3): LinkInfoV2 | undefined {
        const checkMeshs = this.allPipeActors;

        for (let i = 0; i < checkMeshs.length; i++) {
            const guandao = checkMeshs[i];
            if (pipe === guandao) continue;
            const threshold = Math.max(guandao.radius, pipe.radius);

            let graphs = this.guandoToPoint.get(guandao);
            if (!graphs) continue;
            if (graphs.length < 2) continue;


            for (let j = 0; j < graphs.length - 1; j++) {
                const a = graphs[j];
                const b = graphs[j + 1];

                _vec3_1.copy(a.pos);
                _vec3_2.copy(b.pos)

                //和左端靠近
                let distance = start.distanceTo(_vec3_1);
                if (distance <= threshold) {
                    a.isMerged = true;
                    return {
                        from: pipe,
                        pipe: guandao,
                        node: a,
                        replace: true
                    }
                }
                //和右端靠近
                distance = start.distanceTo(_vec3_2);
                if (distance <= threshold) {
                    b.isMerged = true;
                    return {
                        from: pipe,
                        pipe: guandao,
                        node: b,
                        replace: true
                    }
                }

                if (pointOnSegment(start, a.pos, b.pos, threshold, _line3_target)) {
                    let point = new VirtualPipePoint()
                    this.allPoints.push(point);
                    point.isMerged = true;
                    point.pos.copy(_line3_target);
                    graphs.splice(j + 1, 0, point);
                    return {
                        from: pipe,
                        pipe: guandao,
                        node: point,
                        replace: true,
                    }
                }
            }

        }
    }

    private __checkMachine(pipe: GuandaoActor, start: Vector3, end: Vector3): LinkInfo | undefined {
        this.raycast.ray.origin.copy(end);
        this.raycast.ray.direction.copy(_line3_target.copy(end).sub(start)).normalize();
        this.raycast.near = 0;
        this.raycast.far = 0.5;

        for (let i = 0; i < this.machines.length; i++) {
            const machine = this.machines[i];
            let boxinfo = this.boxInfo.get(machine);
            if (!boxinfo) continue;

            let listinfo = [boxinfo.w, boxinfo.b]
            for (let n = 0; n < 2; n++) {
                const e = listinfo[n];
                if (!e) continue;
                let inters = this.raycast.intersectObject(e.mesh);
                if (inters.length > 0) {
                    //console.log(inters[0]);
                    let mesh = inters[0].object as Mesh;
                    return {
                        from: pipe,
                        deviceid: machine.deviceId || "",
                        mesh: mesh,
                        actor: machine,
                    }
                }


                let flag = pointOnSegment(end, e.min, e.max, pipe.radius * 2);
                if (flag) {
                    let mesh = e.mesh;
                    return {
                        from: pipe,
                        deviceid: machine.deviceId || "",
                        mesh: mesh,
                        actor: machine,
                    }
                }

            }
        }
    }

    private __setGuandaoDir(guandao: GuandaoActor, dir: boolean) {
        if (this.__fixedGuanDao.get(guandao)) {
            console.warn("重复修正管道方向: ", this.__fixedGuanDao.get(guandao), "=>", dir);
            return;
        }

        guandao.flowDir = dir;
        if (!dir) {
            this.guandoToPoint.get(guandao)?.reverse();
        }
        this.__fixedGuanDao.set(guandao, dir);
    }

    private __getOrCreateVirtualPipe(guandao: GuandaoActor, dir: PipeDir): VirtualPipe {
        let pipe = new VirtualPipe(guandao, dir)
        if (dir != PipeDir.UNKNOW) {
            this.__setGuandaoDir(guandao, dir == PipeDir.FORWARD ? true : false);
        }
        else {
            pipe.pipeDir = guandao.flowDir ? PipeDir.FORWARD : PipeDir.BACK;
        }
        return pipe;
    }

    private __createVirtualPipePoint(guandao: GuandaoActor) {
        //console.log("start", guandao.uuid)

        function checkHave(list: VirtualPipePoint[], p: VirtualPipePoint) {
            for (let i = 0; i < list.length; i++) {
                const e = list[i];
                if (e.id == p.id) {
                    return true;
                }
            }

            return false;
        }

        let p: VirtualPipePoint | undefined;
        {
            let points = this.guandoToPoint.get(guandao) || [];
            for (let n = 0; n < points.length; n++) {
                let nnxt = points[n];

                if (p) {
                    if (!checkHave(nnxt.in, p)) nnxt.in.push(p);
                    if (!checkHave(p.out, nnxt)) p.out.push(nnxt);
                    let key = `${p.id}-${nnxt.id}`;
                    this.radiusMap.set(key, guandao);
                }
                p = nnxt;
            }
        }


        //console.log("end", guandao.points.length, this.guandoToPoint.get(guandao).length)

    }

    private __fixDirectConnectdPipe(vpipe: GuandaoActor) {
        if (this.__tempFixGuandao.has(vpipe)) return;
        this.__tempFixGuandao.set(vpipe, true);
        let points = this.guandoToPoint.get(vpipe) as any
        if(points && points.length < 2) return;

        {
            //正方向fix
            //如果target出现在其他管道的第一个虚拟点或者最后一个，那说明就是首尾相连
            let target = points[points.length - 1];
            //只需要修复反向的管道，因为默认都是正方向
            let backGuandao: GuandaoActor[] = []
            this.guandoToPoint.forEach((v, k) => {
                if (v.length >= 2) {
                    if (v[v.length - 1] === target) {
                        //逆方向
                        backGuandao.push(k);
                    }
                }
            })
            //数量是1就可以保证是首尾相连，可以推测管道方向了
            if (backGuandao.length == 1) {
                //backGuandao[0].flowDir = false;
                this.__setGuandaoDir(backGuandao[0], false);
                console.log('修复管道方向：' + backGuandao[0].name);
                this.__fixDirectConnectdPipe(backGuandao[0]);
            }
        }
        {
            //反方向fix
            let target = points[0];
            let backGuandao: GuandaoActor[] = []
            this.guandoToPoint.forEach((v, k) => {
                if (v.length >= 2) {
                    if (v[0] === target) {
                        //逆方向
                        backGuandao.push(k);
                    }
                }
            })
            //数量是1就可以保证是首尾相连，可以推测管道方向了
            if (backGuandao.length == 1) {
                //backGuandao[0].flowDir = false;
                this.__setGuandaoDir(backGuandao[0], false);
                console.log('修复管道方向：' + backGuandao[0].name);
                this.__fixDirectConnectdPipe(backGuandao[0]);
            }
        }
    }
    
    getMachinePoint() {
        let info: {
            machine: VirtualMachinePart,
            outPoint: VirtualPipePoint,
            inPoint: VirtualPipePoint,
        }[] = []
        this.machineMeshs.forEach((machine, mesh) => {
            let out = { machine: machine, inPoint: undefined, outPoint: undefined } as any;
            if (machine.inPipe && machine.inPipe.pipe) {
                let points = this.guandoToPoint.get(machine.inPipe.pipe) || []
                //console.log(points)
                if (points.length > 0) {
                    out.inPoint = points[points.length - 1];
                }
            }
            if (machine.outPipe && machine.outPipe.pipe) {
                let points = this.guandoToPoint.get(machine.outPipe.pipe) || []
                //console.log(points)
                if (points.length > 0) {
                    out.outPoint = points[0];
                }
            }

            info.push(out);
        })

        return info;
    }

    private __getPointById(id: number) {
        for (let i = 0; i < this.allPoints.length; i++) {
            const element = this.allPoints[i];
            if (element.trueUsedId == id) {
                return element;
            }

        }
    }
    private __getActors(actorMap: Map<string, ActorJsonList>, cur: VirtualPipePoint) {

        const __genList = (actors: Actor[]) => {
            let result: ActorJsonList = []

            actors.forEach(a => {
                if (a.deviceId) {
                    box.makeEmpty();
                    a.traverse(function (child) {
                        if ((child as Mesh).isMesh) {
                            box.expandByObject(child);
                        }
                    });
                    a.getWorldPosition(_vec3_1);
                    result.push({
                        id: a.uuid,
                        sn: a.sn || "",
                        device: a.deviceId,
                        pos: _vec3_1.toArray(),
                        aabb: {
                            min: box.min.toArray(),
                            max: box.max.toArray(),
                        }
                    })
                }
            })

            return result;
        }
        if (cur && cur.out.length > 0) {
            cur.out.forEach(next => {
                let actors = this.getAttachedPart(cur, next);
                let key = `${cur.trueUsedId}-${next.trueUsedId}`
                if (actorMap.has(key)) {
                    return;
                }

                let result = __genList(actors);
                actorMap.set(key, result)
                this.__getActors(actorMap, next);
            })
        }


        if (cur && cur.in.length > 0) {
            cur.in.forEach(next => {
                let actors = this.getAttachedPart(next, cur);
                let key = `${next.trueUsedId}-${cur.trueUsedId}`
                if (actorMap.has(key)) {
                    return;
                }
                const result = __genList(actors);
                actorMap.set(key, result)
                this.__getActors(actorMap, next);
            })
        }

    }

    getAttachedPart(p1: VirtualPipePoint, p2: VirtualPipePoint) {
        let findGuandao;
        let outactor: Actor[] = []
       
        let key = `${p1.trueUsedId}-${p2.trueUsedId}`;
        findGuandao = this.radiusMap.get(key)
        if (!findGuandao) {
            key = `${p2.trueUsedId}-${p1.trueUsedId}`;
            findGuandao = this.radiusMap.get(key)
        }
        if (findGuandao) {
            for (let i = 0; i < (this.data['__device'] as any).length; i++) {
                const e = (this.data['__device'] as any)[i] as Actor;               
                e.getWorldPosition(_check_pos_1);
                if (pointOnSegment(_check_pos_1, p1.pos, p2.pos, findGuandao.radius * 2.0)) {
                    outactor.push((e as Actor));
                }
            }
        }
        return outactor;
    }
    /**
     * 找出连接了机器的管道
     */
    findLinkMachinePipe(pipe: GuandaoActor) {

        const addMachine = (mesh: Mesh | undefined, deviceid: string, partType: "W" | "B", pipe: { pipe: GuandaoActor, idx: number }, actor: Actor) => {
            if (!mesh) return
            if (!this.machineMeshs.has(mesh)) {
                let m = new VirtualMachinePart();
                m.nid = VirtualPipePoint.PipePointID++;
                this.machineMeshs.set(mesh, m);
                this.allVirtualMachine.push(m)
            }

            let machine = this.machineMeshs.get(mesh);
            if (machine) {
                machine.deviceID = deviceid;
                machine.partType = partType;
                machine.actor = actor;
                machine.pipes.push(pipe)
            }
        }

        if (pipe.points.length < 2) return;

        let p1 = pipe.points[0];
        let p2 = pipe.points[1];
        p1.getWorldPosition(_vec3_1);
        p2.getWorldPosition(_vec3_2);
        let info = this.__checkMachine(pipe, _vec3_2, _vec3_1);
        if (info) {
            if ((info.mesh.material as Material).name.indexOf("IRON_BLACK") >= 0) {
                addMachine(info.mesh, info.deviceid || "", "B", { pipe: pipe, idx: 0 }, info.actor);
            }

            else if ((info.mesh.material as Material).name.indexOf("IRON_WHITE") >= 0) {
                addMachine(info.mesh, info.deviceid || "", "W", { pipe: pipe, idx: 0 }, info.actor);
            }
        }

        p1 = pipe.points[pipe.points.length - 1];
        p2 = pipe.points[pipe.points.length - 2];
        p1.getWorldPosition(_vec3_1);
        p2.getWorldPosition(_vec3_2);
        info = this.__checkMachine(pipe, _vec3_2, _vec3_1);
        if (info) {
            if ((info.mesh.material as Material).name.indexOf("IRON_BLACK") >= 0) {
                addMachine(info.mesh, info.deviceid, "B", { pipe: pipe, idx: pipe.points.length - 1 }, info.actor);
            }

            else if ((info.mesh.material as Material).name.indexOf("IRON_WHITE") >= 0) {
                addMachine(info.mesh, info.deviceid, "W", { pipe: pipe, idx: pipe.points.length - 1 }, info.actor);
            }
        }

    }

    /**
 * 分析机器上的管道
 */
    analyzeMachinePipe(vmachine: VirtualMachinePart) {
        //连接机器的肯定有两根管道，不然就是错误的
        if (vmachine.pipes.length == 2) {
            let pipeA = vmachine.pipes[0];
            let pipeB = vmachine.pipes[1];

            //获取连接部分的控制点
            let p1 = pipeA.pipe.points[pipeA.idx];
            let p2 = pipeB.pipe.points[pipeB.idx];
            //根据管道的上下位置确定管道流向
            let y1 = p1.getWorldPosition(_vec3_1).y;
            let y2 = p2.getWorldPosition(_vec3_1).y;
            let topInfo = y1 > y2 ? pipeA : pipeB;
            let downInfo = topInfo == pipeA ? pipeB : pipeA;

            vmachine.outPipe = this.__getOrCreateVirtualPipe(topInfo.pipe, topInfo.idx == 0 ? PipeDir.FORWARD : PipeDir.BACK);
            vmachine.inPipe = this.__getOrCreateVirtualPipe(downInfo.pipe, downInfo.idx == 0 ? PipeDir.BACK : PipeDir.FORWARD);

            vmachine.outPipe.in = vmachine;
            vmachine.inPipe.out = vmachine;

        }
    }


    onUpdate() {
        //将所有管道拆成虚拟管道，构建无向图
        if (this.workState == WorkState.INIT_GRAPH) {
            //构建所有控制点
            for (let n = 0; n < this.allPipeActors.length; n++) {
                let guandao = this.allPipeActors[n];

                for (let i = 0; i < guandao.points.length; i++) {
                    const point = new VirtualPipePoint();
                    this.allPoints.push(point);
                    guandao.points[i].getWorldPosition(point.pos);

                    let nodes = this.guandoToPoint.get(guandao);
                    if (!nodes) {
                        this.guandoToPoint.set(guandao, [point]);
                        nodes = this.guandoToPoint.get(guandao);
                    }
                    else {
                        nodes.push(point);
                    }

                    if (i < guandao.points.length - 1) {
                        guandao.points[i + 1].getWorldPosition(_path_pos_2)
                        let pump = this.__checkHasWaterInPath(point.pos, _path_pos_2, guandao.radius, _path_pos_3);
                        if (pump) {
                            this.guandaoToPump.set(guandao, pump);
                        }
                    }



                }
            }

            //构建连接点
            for (let n = 0; n < this.allPipeActors.length; n++) {
                let guandao = this.allPipeActors[n];
                let pointList = this.guandoToPoint.get(guandao);
                if (!pointList) continue;

                if (pointList.length < 2) continue;

                let start = pointList[0];
                _check_pos_1.copy(start.pos);

                let addHead, addTail;
                let info = this.__checkPipeV3(guandao, _check_pos_1);
                //说明已经处理过了，啥都不做
                if (info && info.node === start) {

                }
                else if (info) {
                    if (info.replace) {
                        pointList[0].unUsed = true;
                        pointList[0] = info.node;
                    }
                    else {
                        addHead = info.node;
                    }

                }

                let count = pointList.length;
                let end = pointList[count - 1];
                _check_pos_1.copy(end.pos);

                info = this.__checkPipeV3(guandao, _check_pos_1);
                //说明已经处理过了，啥都不做
                if (info && info.node === end) {

                }
                else if (info) {
                    if (info.replace) {
                        pointList[count - 1].unUsed = true;
                        pointList[count - 1] = info.node;
                    }
                    else {
                        addTail = info.node;
                    }
                }

                if (addHead) pointList.splice(0, 0, addHead);
                if (addTail) pointList.push(addTail);
            }

            this.workState = WorkState.FIND_MACHINE_CONNECTED;
            this.workIdx = 0;
        }
        //找出机器连接的管道
        else if (this.workState == WorkState.FIND_MACHINE_CONNECTED) {
            for (let n = 0; n < this.allPipeActors.length; n++) {
                let guandao = this.allPipeActors[n];
                let pointList = this.guandoToPoint.get(guandao);
                if (!pointList) continue;
                if (pointList.length < 2) continue;
                this.findLinkMachinePipe(guandao)
            }
            for (let i = 0; i < this.allVirtualMachine.length; i++) {
                const machine = this.allVirtualMachine[i];
                this.analyzeMachinePipe(machine);
            }

            this.pointToGuandao.clear();
            this.guandoToPoint.forEach((points, guuandao) => {
                points.forEach(p => {
                    this.pointToGuandao.set(p, guuandao);
                })
            })

            this.workState = WorkState.FIX_PIPE_DIR;
            this.workIdx = 0;
        }
        //找出机器连接的管道
        else if (this.workState == WorkState.FIX_PIPE_DIR) {
            if (this.workIdx >= this.__fixDirQueue.length) {
                this.workState = WorkState.INIT_POINT
                this.workIdx = 0;
            }
            else {
                this.__fixDirectConnectdPipe(this.__fixDirQueue[this.workIdx])
                this.workIdx += 1;
            }

        }
        //新的策略，先给每个管道根据控制点创建虚拟点
        else if (this.workState == WorkState.INIT_POINT) {
            //确保guandoToPoint保存的顺序就是管道方向的顺序，默认都是正方向
            this.guandoToPoint.forEach((v, k) => {
                if (!k.flowDir && !this.__fixedGuanDao.has(k)) {
                    v.reverse();
                    this.__fixedGuanDao.set(k, false);
                }

            })
            for (let n = 0; n < this.allPipeActors.length; n++) {
                let guandao = this.allPipeActors[n];
                this.__createVirtualPipePoint(guandao);
            }
            this.workState = WorkState.FIX_RADIUS_MAP;
            //console.log("=========== next ===============")
        }
        else if (this.workState == WorkState.FIX_RADIUS_MAP) {

            this.workState = WorkState.DONE;
            this.getMachinePoint();
        }
    }

    /*导出连接关系图
*/
    exportPipeConnect() {
        let info: {
            machine: VirtualMachinePart,
            outPoint: VirtualPipePoint,
            inPoint: VirtualPipePoint,
            pos: number[];
            aabb: {
                min: number[],
                max: number[]
            }
            actorId: string,
        }[] = []

        this.machineMeshs.forEach((machine, mesh) => {
            let out = { machine: machine, inPoint: undefined, outPoint: undefined, pos: undefined, actorId: undefined, aabb: undefined } as any;
            if (machine.inPipe && machine.inPipe.pipe) {
                let points = this.guandoToPoint.get(machine.inPipe.pipe) || []
                //console.log(points)
                if (points.length > 0) {
                    out.inPoint = points[points.length - 1];
                }
            }
            if (machine.outPipe && machine.outPipe.pipe) {
                let points = this.guandoToPoint.get(machine.outPipe.pipe) || []
                //console.log(points)
                if (points.length > 0) {
                    out.outPoint = points[0];
                }
            }
            mesh.getWorldPosition(_vec3_1);
            out.pos = _vec3_1.toArray();
            out.actorId = machine.actor?.uuid;

            box.makeEmpty();
            machine.actor?.traverse(function (child) {
                if ((child as Mesh).isMesh) {
                    box.expandByObject(child);
                }
            });

            out.aabb = {
                min: box.min.toArray(),
                max: box.max.toArray(),
            }

            info.push(out);
        })

        let outJson = {
            points: {},
            machine: [],
            pipes: {},
            pipeorder: [],
            actors: {}
        } as any

        let checkID = {} as any

        this.allPoints.forEach(p => {
            if (!checkID[p.trueUsedId]) {
                outJson.points[p.trueUsedId] = (p.toJson())
                checkID[p.trueUsedId] = true;
            }
        })

        let actorMap = new Map<string, ActorJsonList>();
        info.forEach(i => {
            outJson.machine.push({
                actorid: i.actorId,
                sn: i.machine.actor?.sn || "",
                device: i.machine.deviceID,
                pos: i.pos,
                aabb: i.aabb,
                in: i.inPoint?.id,
                out: i.outPoint?.id,
                type: i.machine.partType,
            })
            this.__getActors(actorMap, i.inPoint);
            this.__getActors(actorMap, i.outPoint);
        })


        for (const key of actorMap.keys()) {
            let ids = key.split("-");
            if (checkID[parseInt(ids[0])] && checkID[parseInt(ids[1])]) {
                outJson.actors[key] = actorMap.get(key);
            }
        }


        outJson.pipes = {};
        let orderList = []
        for (const key of this.radiusMap.keys()) {
            let ids = key.split("-");
            if (checkID[parseInt(ids[0])] && checkID[parseInt(ids[1])]) {
                outJson.pipes[key] = {
                    radiu: (this.radiusMap.get(key))?.radius,
                    color: (this.radiusMap.get(key))?.color.toJSON()
                }

                let p1 = this.__getPointById(parseInt(ids[0]))
                let p2 = this.__getPointById(parseInt(ids[1]))
                if(p1 && p2) {
                    let order = (p1.pos.y + p2.pos.y) * 0.5
                    orderList.push({
                        key: key,
                        order: order
                    })
                }

            }
        }

        orderList.sort((a, b) => a.order - b.order);
        outJson.pipeorder = []
        orderList.forEach(info => {
            outJson.pipeorder.push(info.key)
        })
        //console.log(outJson);
        return outJson;
    }
}
